﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Routing;
using GC_RF.BusinessEntities.DTOs;
using GC_RF.Domain.Abstractions.Interfaces.Factories;
using GC_RF.Domain.Abstractions.Interfaces.Services;
using GC_RF.Domain.Constants;
using GC_RF.Domain.Helpers;
using GC_RF.Site.Abstractions.Interfaces.Resources;
using GC_RF.Site.Resources;
using Microsoft.Web.Mvc;

namespace GC_RF.Site.Abstractions
{
	public class BaseController : Controller
	{
		#region Variables

		private readonly IUserService _userService;

		private bool _disposeAppContext;

		public IAppContext AppContext { get; private set; }

		#endregion

		#region Properties

		protected IServiceFactory ServiceFactory { get; private set; }

		#endregion

		#region Constructors

		protected BaseController(IServiceFactory serviceFactory)
		{
			if (serviceFactory == null)
				throw new ArgumentNullException(string.Format("serviceFactory"));
			ServiceFactory = serviceFactory;

			_userService = ServiceFactory.CreateUserService();
			if (_userService == null)
				throw new ArgumentNullException(string.Format("userService"));
		}

		#endregion

		#region Protected Methods

		protected bool Execute(Action action)
		{
			try
			{
				action();
				return true;
			}
			//catch (PermissionsValidationException exception)
			//{
			//	var errorMessage = exception.ValidationResult.Errors.Select(o => o.Value).FirstOrDefault();
			//	ModelState.AddModelError("Error", errorMessage);
			//}
			//catch (DataValidationException exception)
			//{
			//	ModelState.AddModelError("Error", exception.ValidationResult.Errors.Select(o => o.Value).FirstOrDefault());
			//}
			//catch (BusinessRulesValidationException exception)
			//{
			//	var errorMessage = exception.ValidationResult.Errors.Select(o => o.Value).FirstOrDefault();
			//	ModelState.AddModelError("Error", errorMessage);
			//}
			catch (ArgumentException exception)
			{
				ModelState.AddModelError("Error", exception.Message.Replace("Parameter name: ", ""));
			}
			//catch (EntityCommandExecutionException exception)
			//{
			//	AnalyzeSqlException(exception);
			//}
			//catch (UpdateException exception)
			//{
			//	AnalyzeSqlException(exception);
			//}
			catch (Exception exception)
			{
				ApplicationLogger.Logger.LoggingExceptionMessage(exception);
				ModelState.AddModelError("Error", "An internal error occured.");
			}
			return false;
		}

		protected T Execute<T>(Func<T> func)
		{
			try
			{
				return func();
			}
			//catch (PermissionsValidationException exception)
			//{
			//	var errorMessage = exception.ValidationResult.Errors.Select(o => o.Value).FirstOrDefault();
			//	ModelState.AddModelError("Error", errorMessage);
			//}
			//catch (DataValidationException exception)
			//{
			//	ModelState.AddModelError("Error", exception.ValidationResult.Errors.Select(o => o.Value).FirstOrDefault());
			//}
			//catch (BusinessRulesValidationException exception)
			//{
			//	var errorMessage = exception.ValidationResult.Errors.Select(o => o.Value).FirstOrDefault();
			//	ModelState.AddModelError("Error", errorMessage);
			//}
			catch (ArgumentException exception)
			{
				ModelState.AddModelError("Error", exception.Message.Replace("Parameter name: ", ""));
			}
			//catch (EntityCommandExecutionException exception)
			//{
			//	AnalyzeSqlException(exception);
			//}
			//catch (UpdateException exception)
			//{
			//	AnalyzeSqlException(exception);
			//}
			catch (Exception exception)
			{
				ApplicationLogger.Logger.LoggingExceptionMessage(exception);
				ModelState.AddModelError("Error", "An internal error occured.");
			}
			return default(T);
		}

		protected bool IsValid { get { return ModelState.IsValid; } }

		public static RedirectToRouteResult RedirectToAction<T>(Expression<Action<T>> action, RouteValueDictionary values = null) where T : Controller
		{
			var body = action.Body as MethodCallExpression;

			if (body == null)
				throw new ArgumentException("Expression must be a method call.");

			if (body.Object != action.Parameters[0])
				throw new ArgumentException("Method call must target lambda argument.");

			var actionName = body.Method.Name;

			var attributes = body.Method.GetCustomAttributes(typeof(ActionNameAttribute), false);
			if (attributes.Length > 0)
			{
				var actionNameAttr = (ActionNameAttribute)attributes[0];
				actionName = actionNameAttr.Name;
			}

			var controllerName = typeof(T).Name;

			if (controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
			{
				controllerName = controllerName.Remove(controllerName.Length - 10, 10);
			}

			var defaults = LinkBuilder.BuildParameterValuesFromExpression(body) ?? new RouteValueDictionary();

			values = values ?? new RouteValueDictionary();
			values.Add("controller", controllerName);
			values.Add("action", actionName);

			foreach (var pair in defaults.Where(p => p.Value != null))
				values.Add(pair.Key, pair.Value);

			return new RedirectToRouteResult(values);
		}

		#endregion

		#region Override Methods

		protected override void OnAuthorization(AuthorizationContext filterContext)
		{
			base.OnAuthorization(filterContext);

			AppContext = Resources.AppContext.Current;

			if (AppContext != null) return;

			_disposeAppContext = true;
			AppContext = new AppContext();
			Resources.AppContext.Current = AppContext;

			if (AppContext.CurrentUser != null
				|| filterContext.HttpContext.User == null
				|| !filterContext.HttpContext.Request.IsAuthenticated)
				return;

			var identity = filterContext.HttpContext.User.Identity;
			if (SessionManager.Get<UserData>(Keys.User) != null)
				AppContext.CurrentUser = SessionManager.Get<UserData>(Keys.User);
			else
				Execute(() => AppContext.CurrentUser = _userService.GetUserByName(identity.Name));
			if (AppContext.CurrentUser != null)
				SessionManager.Set(Keys.User, AppContext.CurrentUser);
		}

		protected override void Dispose(bool disposing)
		{
			base.Dispose(disposing);
			if (!disposing) return;

			if (AppContext == null) return;

			if (_disposeAppContext)
				Resources.AppContext.Current = null;
			AppContext = null;
		}

		#endregion
	}
}